#include "inputs/manager.h"
#include "inputs/command.h"

#include <algorithm>

namespace dw {
	namespace inputs {

void InputManager::initialize(const dw::system::Window& window)
{
	OIS::ParamList pl;
	
	std::ostringstream wnd;
	wnd << (size_t)window.getHandle();

	pl.insert(std::make_pair(std::string("WINDOW"), wnd.str()));

	//Default mode is foreground exclusive..but, we want to show mouse - so nonexclusive
	pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND")));
	pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));

	//This never returns null.. it will raise an exception on errors
	m_inputManager = OIS::InputManager::createInputSystem(pl);

	//Print debugging information
	/*
	unsigned int v = m_inputManager->getVersionNumber();
	std::cout << "OIS Version: " << (v>>16 ) << "." << ((v>>8) & 0x000000FF) << "." << (v & 0x000000FF)
		<< "\nRelease Name: " << m_inputManager->getVersionName()
		<< "\nManager: " << m_inputManager->inputSystemName()
		<< "\nTotal Keyboards: " << m_inputManager->getNumberOfDevices(OIS::OISKeyboard)
		<< "\nTotal Mice: " << m_inputManager->getNumberOfDevices(OIS::OISMouse)
		<< "\nTotal JoySticks: " << m_inputManager->getNumberOfDevices(OIS::OISJoyStick) << std::endl;
	*/

	//List all devices
	static const char *deviceType[6] = { "OISUnknown", "OISKeyboard", "OISMouse", "OISJoyStick", "OISTablet", "OISOther" };

	OIS::DeviceList list = m_inputManager->listFreeDevices();
	for (OIS::DeviceList::iterator i = list.begin(); i != list.end(); ++i)
	{
		dwLogInform( "\tDevice: '%s' Vendor '%s'\n", deviceType[i->first], i->second.c_str());
	}

	m_keyboard = static_cast< OIS::Keyboard* >(m_inputManager->createInputObject(OIS::OISKeyboard, true));
	m_keyboard->setEventCallback(this);
	m_keyboard->setBuffered(false);

	m_mouse = static_cast< OIS::Mouse* >(m_inputManager->createInputObject(OIS::OISMouse, true));
	m_mouse->setEventCallback(this);
	m_mouse->setBuffered(false);

	const OIS::MouseState &ms = m_mouse->getMouseState();
	ms.width = window.getWidth();
	ms.height = window.getHeight();
}

void InputManager::update()
{
	if (m_keyboard != NULL)
	{
		m_keyboard->capture();
	}

	if (m_mouse != NULL)
	{
		m_mouse->capture();
	}
	
	for (CommandsCollection::iterator commandIt = m_commands.begin(); commandIt != m_commands.end(); ++commandIt)
	{
		InputCommand* command = *commandIt;

		command->update(m_keyboard, m_mouse);
	}
}

void InputManager::registerCommand(InputCommand* command)
{
	dwAssert(command != NULL, "Invalid command");
	dwAssert(std::find(m_commands.begin(), m_commands.end(), command) == m_commands.end(), "Command is already registered");

	m_commands.push_back(command);
}

void InputManager::unregisterCommand(InputCommand* command)
{
	dwAssert(command != NULL, "Invalid command");
	dwAssert(std::find(m_commands.begin(), m_commands.end(), command) != m_commands.end(), "Command is not registered");

	m_commands.erase(std::find(m_commands.begin(), m_commands.end(), command));
}

bool InputManager::keyPressed(const OIS::KeyEvent &arg)
{
	dwLogInform("keyPressed { %d : '%s' : '%c' }\n",
		arg.key,
		((OIS::Keyboard*)(arg.device))->getAsString(arg.key).c_str(),
		(char)arg.text);

	return true;
}

bool InputManager::keyReleased(const OIS::KeyEvent &arg)
{
	dwLogInform("keyReleased { %d }\n",
		arg.key);

	return true;
}

bool InputManager::mouseMoved(const OIS::MouseEvent &arg)
{
	const OIS::MouseState& s = arg.state;

	/*
	dwLogInform("mouseMoved: Abs(%d, %d, %d) Rel(%d, %d, %d)\n",
			  s.X.abs, s.Y.abs, s.Z.abs,
			  s.X.rel, s.Y.rel, s.Z.rel);
	*/

	return true;
}

bool InputManager::mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
{
	const OIS::MouseState& s = arg.state;

	dwLogInform("mousePressed button #%d: Abs(%d, %d, %d) Rel(%d, %d, %d)\n",
			  id,
			  s.X.abs, s.Y.abs, s.Z.abs,
			  s.X.rel, s.Y.rel, s.Z.rel);

	return true;
}

bool InputManager::mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
{
	const OIS::MouseState& s = arg.state;
	
	dwLogInform("mouseReleased button #%d: Abs(%d, %d, %d) Rel(%d, %d, %d)\n",
			  id,
			  s.X.abs, s.Y.abs, s.Z.abs,
			  s.X.rel, s.Y.rel, s.Z.rel);

	return true;
}

InputManager::InputManager()
:	m_inputManager(NULL)
,	m_keyboard(NULL)
,	m_mouse(NULL)
{
	;
}

InputManager::~InputManager()
{
	dwAssert(m_commands.empty(), "Did you forget to unregister Commands ?");

	if (m_inputManager != NULL)
	{
		OIS::InputManager::destroyInputSystem(m_inputManager);
	}
}

	} // namespace inputs
} // namespace dw
